
// import WabtModule from "./lib/libwabt";
import { DFA, TokenType, ptns, PL0SyntaxError, PL0SemanticError } from "./common/Constrants";
import { GraphNode, GraphEdge } from "./common/Graph";
import { Token } from "./common/Token";
import { TreeNode, ProcedureNode, SequenceNode, SequenceType, ConditionNode, ExpNode, ItemNode, FactorNode, IDF, idfType } from "./common/Tree";

export class Parser {
    tokens: Token[] = []
    p = 0
    watCode: string[] = []
    wabt: any = null


    constructor() {
        
    }

    init () {
        const promise = require("wabt")()
        promise.then((wabt: any) => {
            this.wabt = wabt
            console.log('编译器初始化完成');
        })
        return promise        
    }
    /**
     * 进行词法分析
     * @param code 源代码
     */
    analyze(code: string) {
        const tokens: Token[] = this.tokens = []
        let state = 0
        let tokenString = ''
        let pos = 0
        let node = DFA.nodes[0]
        let found = false
        function match(node: GraphNode<TokenType | null, GraphEdge<RegExp>>, c: string) {
            for (let edge of node.edges) {
                if (edge.value.test(c)) {
                    tokenString += c
                    state = edge.target
                    found = true
                    break
                }
            }
        }
        for (let c of code) {
            node = DFA.nodes[state]
            found = false
            // 先按照边查找，如果匹配就加入符号串
            match(node, c)  
            // 未能按照规则匹配，终止节点退出，或者抛出错误
            if (!found) {
                if (node.value !== null) {
                    // 终止节点：归结，然后返回初始状态
                    tokens.push(new Token(node.value, tokenString))
                    tokenString = ''
                    node = DFA.nodes[state = 0]
                    // 注意此时一定在查找一次
                    match(node, c)
                    node = DFA.nodes[state]
                } else if (state || !ptns.space.test(c)) {
                    throw PL0SyntaxError.raise(undefined, 0, c)
                }
            }
            pos++
        }
        // 最后归结
        if (tokenString) {
            if (node.value !== null) {
                tokens.push(new Token(node.value, tokenString))
            } else {
                throw PL0SyntaxError.raise(undefined, 0, tokenString)
            }
        }
        return this.tokens = tokens
    }

    look(type: TokenType) {
        const t = this.tokens[this.p]
        if (!t) {
            return null
        } else if (t.type === type) {
            return this.tokens[this.p++]
        }
        return null
    }
    
    next(type: TokenType, errorType = -1, errorValue = '') {
        const t = this.tokens[this.p]
        if (!t) {
            throw PL0SyntaxError.raise(this.tokens[this.p], 1, TokenType[type])
        }
        if (t.type !== type) {
            if (errorType >= 0) throw PL0SyntaxError.raise(this.tokens[this.p], errorType, errorValue) 
        }
        this.p++

        return t
    }

    /**
     * 查找特定标识符是否存在，并返回标识符信息
     * @param tree 开始查找的非终极符语法树
     * @param idf 标识符Token
     * @param type 可返回的标识符类型数组
     * @param error 不存在时是否报错
     * @returns 标识符信息，或者什么都没有
     */
    idfExist (tree: TreeNode, idf: Token, type = [idfType.var], error = true): IDF | null {
        let f = tree.find((node: TreeNode) => {
            if (node instanceof ProcedureNode) {
                if (type.indexOf(idfType.var) !== -1 && node.variables.has(idf.value)) {
                    const global = node.isGlobal()
                    return {
                        type: idfType.var,
                        value: idf.value,
                        isGlobal: global,
                    }
                }
                if (type.indexOf(idfType.const) !== -1 && node.constrants.has(idf.value)) {
                    const global = node.isGlobal()
                    const constValue = node.constrants.get(idf.value)
                    return {
                        type: idfType.const,
                        value: idf.value,
                        isGlobal: global,
                        num: constValue
                    }
                }
                if (type.indexOf(idfType.proc) !== -1 && node.procedures.has(idf.value)) {
                    return {
                        type: idfType.proc,
                        value: idf.value,
                        isGlobal: true,
                    }
                }
            }
            return null
        })
        if (error && f === null) throw PL0SemanticError.raise(0, idf.value)
        return f
    }

    /**
     * 语法分析和语义分析，生成中间代码
     * @returns 中间代码或者错误
     */
    parse() {
        this.p = 0
        try {
            const tree = this.procedure(null)
            this.next(TokenType.POI, 1, '.')
            return this.watCode = tree.toWat()
        } catch (error) {
            throw error
        }
    }

    procedure(parent: ProcedureNode | null, idf: Token | null = null) {
        const tree = new ProcedureNode(parent, idf)
        while (1) {
            if (this.look(TokenType.CON)) {
                while (1) {
                    const idf = this.next(TokenType.IDF, 2, '标识符')
                    this.next(TokenType.EQL, 3)
                    const num = this.next(TokenType.NUM, 4)
                    tree.pushConst(idf.value, num.value)
                    if (this.look(TokenType.CMA)) {
                        
                    } else if (this.look(TokenType.SEM)) {
                        break
                    } else {
                        throw PL0SyntaxError.raise(this.tokens[this.p], 2, ';')
                    }
                }
            } else if (this.look(TokenType.VAR)) {
                while (1) {
                    const idf = this.next(TokenType.IDF, 2, '标识符')
                    tree.pushVar(idf.value)
                    if (this.look(TokenType.CMA)) {
                        
                    } else if (this.look(TokenType.SEM)) {
                        break
                    } else {
                        throw PL0SyntaxError.raise(this.tokens[this.p], 2, ';')
                    }
                }
            } else if (this.look(TokenType.PRO)) {
                // 记录过程：idf
                const idf = this.next(TokenType.IDF, 5, '标识符')
                this.next(TokenType.SEM, 2, ';')
                this.procedure(tree, idf)
                this.next(TokenType.SEM, 6, ';')
            } else {
                this.sequence(tree)
                break
            }
        }
        return tree
    }

    sequence(parent: TreeNode) {
        let tree = null
        let idfs = []
        let idf = this.look(TokenType.IDF) as Token
        if (idf) {
            tree = new SequenceNode(SequenceType.assign, parent)
            this.next(TokenType.CEQ, 7)
            tree.idf = this.idfExist(parent, idf)
            this.expression(tree)
        } else if (this.look(TokenType.IF)) {
            tree = new SequenceNode(SequenceType.branch, parent)

            // 条件语句
            this.condition(tree)
            this.next(TokenType.THN, 11, 'then')
            // 作用域递增
            this.sequence(tree)
            // 作用域递减
            if (this.look(TokenType.ELS)) {
                // 作用域递增
                this.sequence(tree)
                // 作用域停下
            }
        } else if (this.look(TokenType.WHL)) {
            tree = new SequenceNode(SequenceType.while, parent)
            // 循环语句
            this.condition(tree)
            this.next(TokenType.DO, 12, 'do')
            this.sequence(tree)
        } else if (this.look(TokenType.CAL)) {
            tree = new SequenceNode(SequenceType.call, parent)
            // 调用语句
            idf = this.next(TokenType.IDF, 13)
            tree.idf = this.idfExist(parent, idf, [idfType.proc])
        } else if (this.look(TokenType.BGN)) {
            tree = new SequenceNode(SequenceType.combine, parent)

            this.sequence(tree)
            while (1) {
                if (this.look(TokenType.END)) {
                    break
                } else if (this.look(TokenType.SEM)) {
                    this.sequence(tree)
                } else {
                    throw PL0SyntaxError.raise(this.tokens[this.p], 10, ';')
                }
            }
        } else if (this.look(TokenType.RED)) {
            tree = new SequenceNode(SequenceType.read, parent)

            this.next(TokenType.LBR, 9, '(')
            while (1) {
                const idf = this.next(TokenType.IDF, 9, '标识符')
                
                idfs.push(this.idfExist(parent, idf) as IDF)

                if (this.look(TokenType.RBR)) {
                    tree.idfs = idfs
                    break
                } else if (this.look(TokenType.CMA)) {
                } else {
                    throw PL0SyntaxError.raise(this.tokens[this.p], 10, ';')
                }
            }
        } else if (this.look(TokenType.WRI)) {
            tree = new SequenceNode(SequenceType.write, parent)
            this.next(TokenType.LBR, 9, '(')
            
            while (1) {
                let token = this.look(TokenType.IDF)
                if (token) {
                    idfs.push(this.idfExist(parent, token, [idfType.const, idfType.var]) as IDF)
                } else if (token = this.look(TokenType.NUM)) {
                    // 这里放入一个假标识符供数字读取
                    idfs.push({type: idfType.const, value: '', isGlobal: false, num: parseInt(token.value)})
                } else {
                    throw PL0SyntaxError.raise(this.tokens[this.p], 9, '标识符或变量')
                }
                if (this.look(TokenType.RBR)) {
                    tree.idfs = idfs
                    break
                } else if (this.look(TokenType.CMA)) {
                } else {
                    throw PL0SyntaxError.raise(this.tokens[this.p], 10, ';')
                }
            }
        } else if (this.look(TokenType.RPT)) {
            // 重复循环语句
            tree = new SequenceNode(SequenceType.repeat, parent)
            while (1) {
                this.sequence(tree)
                if (this.look(TokenType.UTL)) {
                    this.condition(tree)
                    break
                } else {
                    this.next(TokenType.SEM, 6, ';')
                }
            }
        }
        
        return tree
    }

    condition(parent: TreeNode | null) {
        let tree = new ConditionNode(parent)
        if (this.look(TokenType.ODD)) {
            tree.type = TokenType.ODD
            this.expression(tree)
        } else {
            this.expression(tree)
            if (this.look(TokenType.EQL)) {
                tree.type = TokenType.EQL
                
            } else if (this.look(TokenType.NEQ)) {
                tree.type = TokenType.NEQ
                
            } else if (this.look(TokenType.LES)) {
                tree.type = TokenType.LES
                
            } else if (this.look(TokenType.LEQ)) {
                tree.type = TokenType.LEQ
                
            } else if (this.look(TokenType.GTR)) {
                tree.type = TokenType.GTR
                
            } else if (this.look(TokenType.GEQ)) {
                tree.type = TokenType.GEQ
                
            } else {
                throw PL0SyntaxError.raise(this.tokens[this.p], 14)
            }
            this.expression(tree)
        }
        return tree
    }

    expression (parent: TreeNode | null) {
        let tree = new ExpNode(parent)
        let isAdden = true
        if (this.look(TokenType.ADD)) {
            
        } else if (this.look(TokenType.SUB)) {
            isAdden = false
        } else {

        }
        this.item(tree, isAdden)
        while (1) {
            if (this.look(TokenType.ADD)) {
                isAdden = true
            } else if (this.look(TokenType.SUB)) {
                isAdden = false
            } else {
                break
            }
            this.item(tree, isAdden)
        }
    }

    item(parent: TreeNode | null, isAdden: boolean) {
        let tree = new ItemNode(parent, isAdden)
        this.factor(tree)
        while (1) {
            if (this.look(TokenType.MUL)) {
                tree.isMul.push(true)
            } else if (this.look(TokenType.DIV)) {
                tree.isMul.push(false)
                
            } else {
                break
            }
            this.factor(tree)
        }
    }

    factor(parent: TreeNode | null) {
        let tree = new FactorNode(parent)
        let t: Token
        if (t = this.look(TokenType.IDF)!) {
            let idf = this.idfExist(tree, t, [idfType.const, idfType.var])!
            if (idf.num !== undefined) {
                tree.value = idf.num
            } else {
                tree.idf = idf
            }
        } else if (t = this.look(TokenType.NUM)!) {
            tree.value = t.value
        } else if (this.look(TokenType.LBR)) {
            this.expression(tree)
            this.next(TokenType.RBR, 10, ')')
        } else {
            throw PL0SyntaxError.raise(this.tokens[this.p], 8, TokenType[this.tokens[this.p]?.type])
        }
    }

    /**
     * 正式编译wat代码并执行
     * @param inputs 输入的整数，最大长度63
     * @returns 输出的整数，最大长度63
     */
    compile(inputs: number[]) {
        const features = {
            'exceptions' : true,
            'mutable_globals' : true,
            'sat_float_to_int' : true,
            'sign_extension' : true,
            'simd' : true,
            'threads' : true,
            'multi_value' : true,
            'tail_call' : true,
            'bulk_memory' : true,
            'reference_types' : true,
        }
        if (this.wabt && this.watCode) {
            try {
                // 1. 编译wat为WebAssembly.Module
                let module = this.wabt.parseWat('test.wast', this.watCode.join('\n'), features);
                module.resolveNames();
                module.validate(features);
                let binaryOutput = module.toBinary({log: true, write_debug_names:true});
                let compileLog = binaryOutput.log;
                let binaryBuffer = binaryOutput.buffer;
                let wasm = new WebAssembly.Module(binaryBuffer);
                // 2. 准备内存和 wasmInstance ，并写入输入数值
                const memory = new WebAssembly.Memory({initial:10, maximum:100})
                const i32 = new Int32Array(memory.buffer)
                const wasmInstance = new WebAssembly.Instance(wasm, {js:{ mem: memory }});
                inputs.forEach((num, i) => {i32[i] = num})
                // 3. 导出编译好的 program 并执行
                const program = wasmInstance.exports.program as Function
                program()
                // 4. 导出输出变量
                let output = []
                const length = i32[127] / 4
                for (let index = 0; index < length; index++) {
                    output.push(i32[index + 64])
                }
                return output
            } catch (error) {
                throw error
            }
        } else {
            throw Error('not ready')
        }
    }
}